%%%-------------------------------------------------------------------
%%% @doc  
%%% @end
%%%-------------------------------------------------------------------
-module(dpc_server).

-behaviour(gen_server).
%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------
-include("/usr/local/lib/yaws/include/yaws.hrl").
-include("drproxy_client.hrl").

%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
-export([
	 start_link/0,
	 add_server/2,
	 process/2,
	 dequeue/0,
	 empty_queue/0,
	 status/0,
	 queue_size/0,
	 link_to_server/2,
	 done/0,
	 request_size/0
	 ]).
  
%%--------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------

-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
	terminate/2, code_change/3]).

%%--------------------------------------------------------------------
%% record definitions
%%--------------------------------------------------------------------

-record(state, {servers = [], request_size = 0, queue_size = 0}).

%%--------------------------------------------------------------------
%% macro definitions
%%--------------------------------------------------------------------
-define(SERVER, ?MODULE).

%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% @doc Starts the server.
%% @spec start_link() -> {ok, pid()} | {error, Reason}
%% @end
%%--------------------------------------------------------------------
start_link() ->
	gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

add_server(ServerNode, Server) ->
	gen_server:cast(?MODULE, {add_server, ServerNode, Server}).

process(Server, Bin) ->
	gen_server:cast(?MODULE, {process, Server, Bin}).

empty_queue() ->
	gen_server:cast(?MODULE, empty_queue).

dequeue() ->
	gen_server:cast(?MODULE, dequeue).

status() ->
	gen_server:cast(?MODULE, status).

queue_size() ->
	gen_server:call(?MODULE, requests).

done() ->
  gen_server:cast(?MODULE, done).

request_size() ->
  gen_server:call(?MODULE, request_size).

%%====================================================================
%% gen_server callbacks
%%====================================================================

%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%%                         {ok, State, Timeout} |
%%                         ignore               |
%%                         {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
	process_flag(trap_exit, true),
  start_yaws(),
	lists:foreach(fun (ServerNode) ->
	  spawn(?MODULE, link_to_server, [self(), ServerNode])
	end, drproxy_client:servers()),
	change_logfile(),
	{ok, #state{}}.

%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(requests, _From, State) ->
	{reply, State#state.queue_size, State};

handle_call(request_size, _From, State) ->
  {reply, State#state.request_size, State};

handle_call(Msg, _From, State) ->
	error_logger:error_msg("Invalid call: ~w~n", [Msg]),
	{noreply, State}.

%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({process, Server, Bin}, State) ->
	process_server:process(Server, Bin),
	{noreply, State#state{
	  queue_size = State#state.queue_size + 1,
	  request_size = State#state.request_size + 1
	}};

handle_cast(done, State) ->
  case State#state.request_size > 0 of
    true ->
      {noreply, State#state{request_size = State#state.request_size - 1}};
    false ->
      {noreply, State}
  end;

handle_cast({add_server, ServerNode, Server}, State) ->
	link(Server),
	{noreply, State#state{servers = [{ServerNode, Server} | State#state.servers]}};

handle_cast(empty_queue, State) ->
	{noreply, State#state{queue_size = 0}};

handle_cast(dequeue, State) ->
	case State#state.queue_size > 0 of
		true ->
			{noreply, State#state{queue_size = State#state.queue_size - 1}};
		false ->
			{noreply, State}
	end;

handle_cast(status, State) ->
	error_logger:info_msg("Servers: ~w~nRequest Size: ~w~nQueue Size: ~w~n", [State#state.servers, State#state.request_size, State#state.queue_size]),
	{noreply, State};

handle_cast(Msg, State) ->
	error_logger:error_msg("Invalid cast: ~w~n", [Msg]),
	{noreply, State}.

%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%%                                       {noreply, State, Timeout} |
%%                                       {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({'EXIT', Pid, _Reason}, State) -> 
	case lists:keymember(Pid, 2, State#state.servers) of
		true ->
			spawn(?MODULE, link_to_server, [self(), node(Pid)]),
			{noreply, State#state{servers = lists:delete({node(Pid), Pid}, State#state.servers)}};
		false ->
			{noreply, State}
	end;

handle_info({requests, From}, State) ->
	From ! {requests_response, State#state.queue_size},
	{noreply, State};

handle_info(restart_all, State) ->
	resource_server:restart_all(),
	{noreply, State};

handle_info(Info, State) ->
	error_logger:error_msg("Invalid info: ~w~n", [Info]),
	{noreply, State}.

%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
	ok.

%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) -> {ok, State}.

link_to_server(ProcessLoop, ServerNode) ->
	Server = spawn(ServerNode, dp_server, client, []),
	Server ! {register, ProcessLoop, node(), self()},

	receive
		{ok, ServerNode} ->
			error_logger:info_msg("Found new server: ~w~n", [ServerNode]),
			add_server(ServerNode, Server);
		X -> error_logger:error_msg("Unexpected message: link_to_server - ~p~n", [X])
	after 2000 ->
		link_to_server(ProcessLoop, ServerNode)
	end.

%%====================================================================
%%% Internal functions
%%====================================================================

change_logfile() ->
	case regexp:split(atom_to_list(node()), "@") of
		{ok, [Host | _]} ->
		    error_logger:logfile(close),
		    case error_logger:logfile({open, "/var/log/drproxy." ++ Host ++ ".log"}) of
				ok ->
					true;
				_ ->
					error_logger:logfile(close),
					error_logger:logfile({open, "/tmp/drproxy." ++ Host ++ ".log"})
			end,
			case file:open("/tmp/drproxy." ++ Host ++ ".pid", [write]) of
				{ok, File} ->
					file:write(File, os:getpid()),
					file:close(File);
				_ ->
					false
			end;
		_ ->
			true
	end.

start_yaws() ->
  {ok, Logdir} = application:get_env(drproxy_client, logdir),
  {ok, Basedir} = application:get_env(drproxy_client, basedir),
  
	yaws:start_embedded(Basedir ++ "/drproxy_client/docroot", [
		{servername, "localhost"},
		{listen, {0,0,0,0}},
		{port, drproxy_client:config(webport_client)},
		case drproxy_client:config(auth) of
			false ->
				{};
			User ->
			    case string:tokens(atom_to_list(User), ":") of
				[Name, Passwd] ->
			    	error_logger:error_msg("Starting with authentication: ~s pass ~s~n", [Name, Passwd]),
				    {authdirs, [{auth,["/"],"Auth","Basic",[{Name, Passwd}],[],false}]};
				false ->
				    error_logger:error_msg("invalid username password for auth--should be user:pass, instead was: ~w~n", [User]),
					{}
			    end
		end
	], [
		{logdir, Logdir}
	]).
